using System;
using System.Collections.Generic;
using System.Text;

namespace GuavaData
{
    public class ApplyOp : Operator
    {
        // Function application relational operator: input columns, output columns, and the function to apply

        #region Fields
        Operator child;
        public Operator Child
        {
            get { return child; }
            set { child = value; }
        }

        List<string> input;
        public List<string> Input
        {
            get { return input; }
            set { input = value; }
        }

        List<string> output;
        public List<string> Output
        {
            get { return output; }
            set { output = value; }
        }

        Function f;
        public Function F
        {
            get { return f; }
            set { f = value; }
        }
        #endregion Fields

        public ApplyOp(Operator child, IEnumerable<string> input, IEnumerable<string> output, Function f)
        {
            this.child = child;
            child.parent = this;
            this.input = new List<string>(input);
            this.output = new List<string>(output);
            this.f = f;

            // Dummy check: input column count = function input count
            if (this.input.Count != f.inputs.Count)
                throw new Exception("Number of function inputs does not match the number of input columns.");

            // Dummy check: output column count = function output count
            if (this.output.Count != f.outputs.Count)
                throw new Exception("Number of function outputs does not match the number of output columns.");

            // Assign columns, and if input domain does not match, throw an error
            this.exposedColumns = new List<string>();
            this.exposedDomains = new List<Domain.Domain>();
            for (int i = 0; i < child.exposedColumns.Count; i++)
            {
                string name = child.exposedColumns[i];
                if (this.input.Contains(name))
                {
                    // Check to see if domains match
                    if (!(f.inputs[this.input.IndexOf(name)].Equals(child.exposedDomains[i])))
                        throw new Exception("Domains, " + f.inputs[this.input.IndexOf(name)] +", do not match, "+child.exposedDomains[i]+", for column " + name + ".");
                }
                else
                {
                    // Propagate non-function column domains
                    this.exposedColumns.Add(name);
                    this.exposedDomains.Add(child.exposedDomains[i]);
                }
            }

            // Add the output function column domains
            for (int i = 0; i < this.output.Count; i++)
            {
                this.exposedColumns.Add(this.output[i]);
                this.exposedDomains.Add(f.outputs[i]);
            }
        }

        public override SqlText Visit(Provider qv)
        {
            return qv.Transform(this);
        }
        public override void Visit(Transform t, Command c)
        {
            t.Expand(this, c);
        }
        public override Operator Clone()
        {
            return new ApplyOp(this.child.Clone(), this.input, this.output, this.f);
        }

        public override void NotifyChange(Operator old, Operator newOp)
        {
            if (child == old)
            {
                child = newOp;
                newOp.parent = this;
            }
        }

        public override void Visit(Channel c, string table)
        {
            c.MapAliases(this, table);
        }

    }
}
